package com.theaf.core.utils;

import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.sql.Timestamp;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;


/**
    * 对象操作工具类
    * 支持取Map,get,set名称要符合规范
    * @author songwenke
    * @date 2017-11-17
    *
    */
public class BeanUtils {
    //protected static LogUtils logger= LogUtils.getLog(DictionaryHelper.class);

    public static void setProperty(Object bean,String name,Object v) throws Exception {
        boolean isMap = false;
        for(Class c:bean.getClass().getInterfaces()){
            if(c.equals(Map.class)){
                isMap = true;
            }
        }
       if(isMap)
       {
           //setProperty((Map)bean,name,v);
           ((Map)bean).put(name,v);
           return;
       }
        String mname = "set" + name.substring(0,1).toUpperCase()  + name.substring(1);
        Class beanClass = bean.getClass();
        Method m = null;
        try{
             m = getMethod(beanClass,mname,v);
        }catch (Exception e){
            Class vClass = beanClass.getDeclaredField(name).getType();
            m = beanClass.getMethod(mname,new Class[]{vClass});
            v = transform(v,vClass);
        }
        try {
            m.invoke(bean, v);
        }catch(Exception e){
            if(bean instanceof Map){
                ((Map)bean).put(name,v);
            }else{
                throw e;
            }
        }
    }
    
    private static Method getMethod(Class c,String methodName,Object v)
    {
    	Method m = null;
    	Class vclass = v.getClass();
    	try
    	{
    		m = c.getMethod(methodName,vclass);
    	}catch(Exception e)
    	{
    		m = MethodHelper.getMethod(c, methodName,new Object[]{v});
    	}
    	return m;
    }
   /**
    * 
    * 将对象转成封装类型。
    * 如果类型一致则直接返回。
    * 
    * 比如：通过 isAssignableFrom 对比
    * @param o
    * @param to
    * @return 
    * Object    返回类型
    * @throws
    */
public static Object transform(Object o,Class to) throws Exception {
       if(o==null) {
           return null;
       }
       if( to.isAssignableFrom(o.getClass()) ) {
           return o;
       }
       if( double.class.isAssignableFrom(to) || Double.class.isAssignableFrom(to) )
       {
            o = Double.parseDouble(o.toString());
       }
       else if( int.class.isAssignableFrom(to) || Integer.class.isAssignableFrom(to) )
       {
           o = Integer.parseInt(o.toString());
       }
       else if( Date.class.isAssignableFrom(to) )
       {
    	   SimpleDateFormat sf= new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
           try {
			  o = sf.parse(o.toString());
		   } catch (ParseException e) {
				//e.printStackTrace();
		   }
       }
       else if( String.class.isAssignableFrom(to) )
       {
           return o.toString();
       }else {
           Object ret = to.newInstance();
           BeanInfo beanInfo = Introspector.getBeanInfo(to);
           for(PropertyDescriptor propertyDescriptor: beanInfo.getPropertyDescriptors()){
               String fieldname = propertyDescriptor.getName();
               PropertyDescriptor srcPropertyDescriptior = new PropertyDescriptor(fieldname,o.getClass());
               try {
                   Object ob = srcPropertyDescriptior.getReadMethod().invoke(o);
                   propertyDescriptor.getWriteMethod().invoke(ret);
               }catch(Exception e){
                   e.printStackTrace();
                   //logger.debug("BeanUtils.transform->"+fieldname+"->"+e.getMessage());
               }
           }
           return ret;
       }
       return o;
   }

    /**
     * 集合数据转化
     * @param collection 原集合
     * @param to 转化的类型
     * @return
     * @throws Exception
     */
   public static List transforms(Collection collection,Class to) throws Exception{
        List list = new ArrayList();
        Iterator iterator = collection.iterator();
        while(iterator.hasNext()){
            Object src = iterator.next();
            list.add(transform(src,to));
        }
        return list;
   }

public static String transform(Object o) 
{
   String v = null;
    if( o instanceof Date)
    {
 	   if( o instanceof Timestamp )
 	   {
 		   v = DateUtils.dateToString((Date)o, DateUtils.YYYYMMDDHHMMSS);
 	   }
 	   else
 	   {
 		   v = DateUtils.dateToString((Date)o, DateUtils.YYYYMMDD);
 	   }
    }
    return v;
}
   
    public static Object getProperty(Object bean,String name) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        try {
            String mname = "get" + name.substring(0, 1).toUpperCase() + name.substring(1);
            Method m = bean.getClass().getMethod(mname);
            Object v = m.invoke(bean);
            if(v==null){
                if( bean instanceof  Map ) {
                    return getMapProperty(bean, name);
                }else{
                    return null;
                }
            }else{
                return v;
            }
        }catch(Exception e){
            if( bean instanceof  Map ) {
                return getMapProperty(bean, name);
            }
            throw e;
        }
    }

    /**
     * 获取属性类型
     * @param bean
     * @param name
     * @return
     * @throws NoSuchFieldException
     */
    public static Class getPropertyType(Object bean,String name) throws NoSuchFieldException {
       return bean.getClass().getDeclaredField(name).getType();
    }

    public static Object getMapProperty(Object bean,Object k) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {

        return ((Map)bean).get(k);
    }
    
	public static boolean isPrimitive(Object o)
	{
		return o.getClass().isPrimitive() || 
				o instanceof Integer || 				
				o instanceof Long ||
				o instanceof Double || 
				o instanceof String || 
				o instanceof Date || 
				o instanceof Boolean || 
				o instanceof BigDecimal ||
				o instanceof BigInteger ||
				o instanceof String[] ||
				o instanceof Integer[] ||
				o instanceof Long[] ||
				o instanceof Double[]||
                o instanceof  Byte[]||
                o instanceof Timestamp;
	}

    /**
     * map转对象
     * @param map
     * @param beanClass
     * @return
     * @throws Exception
     */
    public static Object mapToObject(Map<String, Object> map, Class<?> beanClass) throws Exception {
        if (map == null) {
            return null;
        }
        Object obj = beanClass.newInstance();

        BeanInfo beanInfo = Introspector.getBeanInfo(obj.getClass());
        PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
        for (PropertyDescriptor property : propertyDescriptors) {
            Method setter = property.getWriteMethod();
            if (setter != null) {
                setter.invoke(obj, map.get(property.getName()));
            }
        }
        return obj;
    }

    /**
     * 对象转MAP
     * @param obj
     * @return
     * @throws Exception
     */
    public static Map<String, Object> objectToMap(Object obj) throws Exception {
        if(obj == null) {
            return null;
        }
        Map<String, Object> map = new HashMap<String, Object>();
        BeanInfo beanInfo = Introspector.getBeanInfo(obj.getClass());
        PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
        for (PropertyDescriptor property : propertyDescriptors) {
            String key = property.getName();
            if (key.compareToIgnoreCase("class") == 0) {
                continue;
            }
            Method getter = property.getReadMethod();
            Object value = getter!=null ? getter.invoke(obj) : null;
            map.put(key, value);
        }
        return map;
    }

    /**
     * 对象转PropertyInfo集合
     * @param obj
     * @return
     * @throws Exception
     */
    public static List<PropInfo> objectToPropInfos(Object obj) throws Exception {
        if(obj == null) {
            return null;
        }
        List<PropInfo> pis = new ArrayList();
        Field[] fields = obj.getClass().getDeclaredFields();
        for(Field field:fields){
            PropInfo pi = new PropInfo();
            pi.setName(field.getName());
            Object v = field.get(obj);
            if(v!=null) {
                if(BeanUtils.isPrimitive(v)) {
                    pi.setValue((Serializable)v);
                }else {
                    pis.addAll(objectToPropInfos(v));
                    continue;
                }
            }else {
                continue;
            }
            ApiModelProperty apiModelProperty = field.getAnnotation(ApiModelProperty.class);
            pi.setTitle(apiModelProperty.value());
            pis.add(pi);
        }
        return pis;
    }

    @Data
    public static class PropInfo implements Serializable{
        private String name;
        private String title;
        private Serializable value;
    }

    /**
     * 线性结构转树形结构
     * @param list 线性数据
     * @param idField ID字段
     * @param pidField 父ＩＤ字段
     * @param listField　集合字段
     * @param id 项级ID
     * @return
     */
    public static Collection lineToTree(Collection list,String idField,String pidField,String listField,Object id){
        try {
            Collection rets = list.getClass().newInstance();
            List haves = new ArrayList();
            int length = list.size();
            boolean b = true;
            while(b){
                b = false;
                for(Object item:list){
                    if(!haves.contains(item)&&getChildList(rets,idField,pidField,listField,item,id)!=null) {
                        haves.add(item);
                        b = true;
                    }
                }
            }
            return rets;
        }catch (Exception e){
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 线性结构转树形结构
     * @param list 线性数据
     * @param idField ID字段
     * @param pidField 父ＩＤ字段
     * @param listField　集合字段
     * @param pid 父级ID
     * @return
     */
    public static Collection lineToTreeByChild(Collection list,String idField,String pidField,String listField,Object pid){
        try {
            Collection rets = list.getClass().newInstance();
            List haves = new ArrayList();
            int length = list.size();
            boolean b = true;
            while(b){
                b = false;
                for(Object item:list){
                    if(!haves.contains(item)&&getChildListByPid(rets,idField,pidField,listField,item,pid,true)!=null) {
                        haves.add(item);
                        b = true;
                    }
                }
            }
            return rets;
        }catch (Exception e){
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 树形数据过滤
     * @param list
     * @param listField
     * @param filter
     * @return
     */
    public static Collection treeFilter(Collection list, String listField, TreeFilter filter){
        try {
            Collection newList = list.getClass().newInstance();
            for (Object item : list) {
                if(cycleFilter(item,listField,filter)) {
                    newList.add(item);
                }
            }
            return newList;
        }catch (Exception e){
            e.printStackTrace();
        }
        return null;
    }

    private static boolean cycleFilter( Object obj,String listField,TreeFilter filter){
        try {
            if(obj==null){
                return false;
            }
            boolean find = false;
            Collection list = (Collection) getProperty(obj, listField);
            Collection newList = list.getClass().newInstance();
            for (Object item : list) {
                if(cycleFilter(item,listField,filter)){
                    //如果子集找到才加
                    newList.add(item);
                    find = true;
                }
            }
            setProperty(obj,listField,newList);
            if(!find){
                return filter.filter(obj);
            }else {
                return true;
            }
        }catch (Exception e){
            e.printStackTrace();
        }
        return false;
    }

    private static Object getChildListByPid(Collection list,String idField,String pidField,String listField,Object obj,Object pid,boolean start){
        try {
            Object cpid = getProperty(obj, pidField);
            if (start&&((pid == null && cpid == null) || (pid!=null&&pid.equals(cpid)))) {
                list.add(obj);
                return obj;
            }
            for (Object item : list) {
                String id = (String)getProperty(item, idField);
                if ((id == null && cpid == null) || (cpid!=null&&cpid.equals(id))) {
                    Collection child = (Collection) getProperty(item, listField);
                    if(child==null){
                        setProperty(item,listField,list.getClass().newInstance());
                        child = (Collection) getProperty(item, listField);
                    }
                    child.add(obj);
                    return obj;
                } else {
                    Collection child = (Collection) getProperty(item, listField);
                    if(child==null){
                        setProperty(item,listField,list.getClass().newInstance());
                        child = (Collection) getProperty(item, listField);
                    }
                    Object c = getChildListByPid(child, idField, pidField,listField, obj,null,false);
                    if (c != null) {
                        return c;
                    }
                }
            }
        }catch (Exception e){
            e.printStackTrace();
        }
        return null;
    }

    private static Object getChildList(Collection list,String idField,String pidField,String listField,Object obj,Object id){
        try {
            Object cid = getProperty(obj, idField);
            if ((id == null && cid == null) || (cid!=null&&cid.equals(id))) {
                list.add(obj);
                return obj;
            }
            Object pid = getProperty(obj, pidField);
            for (Object item : list) {
                id = getProperty(item, idField);
                if ((id == null && pid == null) || (pid!=null&&pid.equals(id))) {
                    Collection child = (Collection) getProperty(item, listField);
                    if(child==null){
                        setProperty(item,listField,list.getClass().newInstance());
                        child = (Collection) getProperty(item, listField);
                    }
                    child.add(obj);
                    return obj;
                } else {
                    Collection child = (Collection) getProperty(item, listField);
                    if(child==null){
                        setProperty(item,listField,list.getClass().newInstance());
                        child = (Collection) getProperty(item, listField);
                    }
                    Object c = getChildList(child, idField, pidField,listField, obj,null);
                    if (c != null) {
                        return c;
                    }
                }
            }
        }catch (Exception e){
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 树形数据转换成线性数据
     * @param list
     * @param idField
     * @param pidField
     * @param listField
     * @return
     */
    public static Collection treeToLine(Collection list,String idField,String pidField,String listField){
        try {
            Collection rets = list.getClass().newInstance();
            for (Object item : list) {
                getChildItem(item,idField,pidField,listField,rets,null);
                setProperty(item, pidField, null);
                rets.add(item);
            }
            return rets;
        }catch (Exception e){
            e.printStackTrace();
            return null;
        }
    }

    private static void getChildItem(Object p,String idField,String pidField,String listField,Collection best,Object pid){
        try {
            Collection list = (Collection) getProperty(p, listField);
            if(list!=null&&list.size()>0) {
                for (Object item : list) {
                    if (best.contains(item)) {
                        continue;
                    }
                    Object nPid = getProperty(p,idField);
                    getChildItem(item,idField,pidField,listField,best,nPid);
                    if(!best.contains(item)){
                        setProperty(item, pidField, nPid);
                        best.add(item);
                    }
                }
            }else{
                setProperty(p, pidField, pid);
                best.add(p);
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }


    public static void main(String str[]) throws Exception
	{
	    /*MapEx a = new MapEx();
	    BeanUtils.setProperty(a,"name2","adfasfd"); ;
	    System.out.println("getName2:"+a.getName());
        System.out.println("get('name2'):"+a.get("name2"));
	    System.out.println("all:"+BeanUtils.getProperty(a,"name2"));*/
	}
}

